﻿using LambdaToSql.FrameWork;
using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Linq.Expressions;
using LambdaToSql.Extended;
using Newtonsoft.Json;
using System.Diagnostics;

namespace LambdaToSql.Realization
{
    internal class Updateable<T> : LambdaToSql.Interface.IUpdateable<T> where T : class, new()
    {
        public virtual LambdaToSql.EntityModel.DbContext Context { get; set; }

        internal virtual LambdaToSql.EntityModel.UpdateEntity<T> Entity { get; set; }

        public Updateable(LambdaToSql.EntityModel.DbContext context)
        {
            this.Context = context;
            //this.Entity = new LambdaToSql.EntityModel.UpdateEntity<T>(this.Context);

            string assemblyName = "LambdaToSql";
            string fullTypeName = string.Format("LambdaToSql.EntityModel.{0}.{1}`1", this.Context.SqlType.ToString(), "UpdateEntity");
            Type objType = Assembly.Load(assemblyName).GetType(fullTypeName);
            Type type = objType.MakeGenericType(typeof(T));
            Entity = (LambdaToSql.EntityModel.UpdateEntity<T>)Activator.CreateInstance(type, new object[] { this.Context });
        }

        public virtual LambdaToSql.Interface.IUpdateable<T> Update(T obj)
        {
            this.Entity.Obj = obj;

            var tableName = this.Entity.TableName;
            var properties = this.Entity.Properties;

            var primaryName = this.Context.Table[tableName].PrimaryName;
            var pi = properties.Where(ex => ex.Name == primaryName).FirstOrDefault();

            object name = pi.Name;
            string value = Convert.ToString(pi.GetValue(obj, null));

            if (!string.IsNullOrEmpty(value))
            {
                var ParaName = "@para" + this.Entity.Parameters.Count();
                if (this.Entity.Where.Length > 0)
                {
                    this.Entity.Where.Append(" and ");
                }
                this.Entity.Where.Append(primaryName + "=" + ParaName);
                this.Entity.Parameters.Add(new LambdaToSql.EntityModel.DataParameter(ParaName, value));
            }
            return this;
        }

        public virtual LambdaToSql.Interface.IUpdateable<T> UpdateColumns(Expression<Func<T, object>> exp)
        {
            if (exp != null)
            {
                var obj = new LambdaRouter(this.Entity.Parameters);
                string str = obj.ExpressionRouter(exp);
                this.Entity.UpdateColumns.AddRange(str.Split(',').ToList());
            }
            return this;
        }

        public virtual LambdaToSql.Interface.IUpdateable<T> IgnoreColumns(Expression<Func<T, object>> exp)
        {
            if (exp != null)
            {
                var obj = new LambdaRouter(this.Entity.Parameters);
                string str = obj.ExpressionRouter(exp);
                this.Entity.IgnoreFields.AddRange(str.Split(',').ToList());
            }
            return this;
        }
        public virtual LambdaToSql.Interface.IUpdateable<T> SetColumns(Expression<Func<T, object>> exp)
        {
            if (exp != null)
            {
                var obj = new LambdaRouter(this.Entity.Parameters);
                string str = obj.ExpressionRouter(exp);
                this.Entity.UpdateColumns.AddRange(str.Split(',').ToList());
            }
            return this;
        }

        public virtual LambdaToSql.Interface.IUpdateable<T> Where(Expression<Func<T, bool>> expression)
        {
            string result = string.Empty;
            if (expression != null)
            {
                Expression exp = expression.Body as Expression;

                var obj = new LambdaRouter(this.Entity.Parameters);
                var str = obj.ExpressionRouter(exp);
                this.Entity.Parameters = obj.Parameters;
                if (this.Entity.Where.Length > 0)
                {
                    this.Entity.Where.Append(" and ");
                }
                this.Entity.Where.Append(obj.Result);
            }
            return this;
        }

        public virtual int ExecuteNonQuery()
        {
            //var DbHelper = new LambdaToSql.DbHelper.Factory() { Context = this.Context }.IDBhelper;
            var DbHelper = this.Context.DbHelper;

            var tableName = this.Entity.TableName;
            var properties = this.Entity.Properties.ToList();
            var primaryName = this.Context.Table[tableName].PrimaryName;


            if (this.Entity.UpdateColumns.Count > 0)
            {
                properties = properties.Where(ex => this.Entity.UpdateColumns.Contains(ex.Name)).ToList();
            }
            if (this.Entity.IgnoreFields.Count > 0)
            {
                properties = properties.Where(ex => !this.Entity.IgnoreFields.Contains(ex.Name)).ToList();
            }
            foreach (PropertyInfo pi in properties)
            {
                var name = pi.Name;
                var value = pi.GetValue(this.Entity.Obj, null);
                if (value == null)
                {
                    value = DBNull.Value;
                }
                string properName = name.ToString().ToLower();
                if (value != DBNull.Value)
                {
                    if (name != primaryName)
                    {
                        var ParaName = "@para" + this.Entity.Parameters.Count;
                        this.Entity.Fields.Add(name.ToString() + "=" + ParaName);
                        this.Entity.Parameters.Add(new LambdaToSql.EntityModel.DataParameter(ParaName, value));
                    }
                }
            }

            var i = DbHelper.ExecuteNonQuery(this.Entity.SqlStr, this.Entity.Parameters.ToArray());

            return Convert.ToInt32(i);
        }

        public virtual LambdaToSql.EntityModel.Result<int> ExecuteNonQuery(bool debug)
        {
            var sw = new Stopwatch();
            sw.Start();

            var i = this.ExecuteNonQuery();

            var runTime = sw.ElapsedMilliseconds;
            sw.Stop();

            var values = JsonConvert.SerializeObject(this.Entity.Parameters.Select(ex => new { ex.Value, ex.Name }).ToList());
            var result = new LambdaToSql.EntityModel.Result<int>()
            {
                Data = i,
                ResultValue = i,
                Method = "ExecuteNonQuery",
                RunTime = (int)runTime,
                DeBug = string.Format("执行语句:{0}<br />值:{1}<br />执行时间:{2}ms<br />执行结果:{3}", this.Entity.SqlStr, values, runTime, i)
            };
            return result;
        }
    }
}
